package KademliaDht

import (
	"os"
	"net"
	"rpc"
	"gob"
	"math"
	"rand"
	"time"
	"strconv"
//	"fmt"
)

type KadNode struct {
	K			int
	Alpha		int
	MyContact	*KadContact
	Buckets		*KadBucketList
	KeyValues	map[uint64] []byte
	KeyTimes	map[uint64] int64
	Bootstrap	*KadContact
	Timer		*time.Timer
}

var _typesRegistered = false

func registerTypesGob() os.Error {
	if ( !_typesRegistered ) {
		// KadContact
		gob.Register( &KadContact{ 0, "", 0 } )
		// KadKeyValue
		gob.Register(  new_KadKeyValue ( &KadContact{ uint64( 0 ), "", 0 }, 0, make( []byte, 0, 0 ) ) )
		// KadFindNodeResult
		gob.Register(  new_KadFindNodeResult ( make( []*KadContact, 0, 0 ) ) )
		// KadFindValueResult
		gob.Register( new_KadFindValueResult( make( []byte, 0, 0 ), make( []*KadContact, 0, 0 ) ) )
		// KadKeyWrapper
		gob.Register( new_KadKeyWrapper( &KadContact{ uint64( 0 ), "", 0 }, 0 ) )
		
		_typesRegistered = true
	}
	return nil
}

func New_KadNode_AutoId( ip string, k int, alpha int, bootstrap *KadContact ) ( kn *KadNode, error os.Error ) {
	// Creates KadNode Object
	kn = &KadNode { 
		k						, 
		alpha					, 
		nil						, 
		New_KadBucketList( k )	, 
		map[uint64] []byte { }	,
		map[uint64] int64 { }	,
		bootstrap				,
		nil						,
	}
	
	port, error := kn.startRpc()
	if error != nil {
		return nil, error
	}
	
	id := GenerateKey( ip + strconv.Itoa( port ) )
	
	// Set MyContact Port
	kn.MyContact = &KadContact{ id, ip, port }
	
	kn.join( bootstrap )
	
	kn.startTimer()

	return kn, nil	
}

// Returns a new KadNode object.
func New_KadNode( ip string, id uint64, k int, alpha int, bootstrap *KadContact ) ( kn *KadNode, error os.Error ) {

	// Creates KadNode Object
	kn = &KadNode { 
		k						, 
		alpha					, 
		nil						, 
		New_KadBucketList( k )	, 
		map[uint64] []byte { }	,
		map[uint64] int64 { }	,
		bootstrap				,
		nil						,
	}
	
	port, error := kn.startRpc()
	if error != nil {
		return nil, error
	}
	
	// Set MyContact Port
	kn.MyContact = &KadContact{ id, ip, port }
	
	kn.join( bootstrap )
	
	kn.startTimer()

	return kn, nil
}

func ( kn *KadNode ) startRpc() ( port int, error os.Error ) {
	// Starts RPC Service
	server := rpc.NewServer()
	server.Register ( kn )
	l, e := net.ListenTCP ( "tcp", &net.TCPAddr{ make([]byte, 0, 0), 0 } )
	if ( e != nil ) {
		return 0, e
	}
	go server.Accept( l )
	addr, _ := l.Addr().(* net.TCPAddr)
	
	// Register Kademlia types to use them in RPC calls
	registerTypesGob()
	
	return addr.Port, nil
}

func ( kn *KadNode ) startTimer() {
	refreshMap := func() {
		r := make( []uint64, 0, 0 )
		t := time.Nanoseconds()
		for k, h := range kn.KeyTimes {
			if ( ( float64( t ) - float64( h ) ) / float64( 3600000000000 ) ) > 1 {
				r = append( r, k )
			}
		}
		for _, k := range r {
			kn.KeyValues[k] = nil, false
			kn.KeyTimes[k] = 0, false
		}
		kn.startTimer()
	}

	kn.Timer = time.AfterFunc( 3600000000000, refreshMap )
}

// Joins the network.
func ( kn *KadNode ) join( bootstrap *KadContact ) {
	if bootstrap != nil {
		kn.Buckets.Spotted( bootstrap, Distance( kn.MyContact.Id, bootstrap.Id ) )
	
		// Lookup for himself
		sh := make( []*KadContact, 0, 0 )
		sh = append( sh, bootstrap )
		nl := New_NodeLookup( kn, kn.MyContact.Id, sh )
		nl.Find_Node()
		
		// Refreshe's bucketlist
		kn.refreshBuckets()
	}
}

// Send's Lookus for nodes in every bucket
func ( kn *KadNode ) refreshBuckets() {
	finish := false
	//j := 0
	for i := 0; !finish; i++ {
		if kn.Buckets.Len() <= i || kn.Buckets.At( i ).Len() < kn.K {
			minDistance := int64( math.Pow( 2, float64( i ) ) )
			maxDistance := int64( math.Pow( 2, float64( ( i + 1 ) ) ) - 1 )
		
			rand.Seed( time.Nanoseconds() )
		
			dist := uint64( rand.Int63n( maxDistance - minDistance  ) + minDistance )
			
			sh, _ := kn.Buckets.FindBucket( dist )
			nl := New_NodeLookup( kn, InvDistance( kn.MyContact.Id, dist ), sh )
			nl.Find_Node()
			
			/*if kn.MyContact.Id == 300 {
				fmt.Printf( "%d -> %d\n", dist, InvDistance( kn.MyContact.Id, dist ) )
				for _, c := range nl.Shortlist {
					fmt.Printf( "\t%d\n", c.Id )
				}
			}*/
			
			if kn.Buckets.Len() <= i {
				//if j > 5 {
					finish = true
				//} else {
				//	j++
				//}
			}// else {
			//	j = 0
			//}
		}
	}
}

// The sender of the STORE RPC provides a key and a block of data and requires that the recipient store the data and make it available for later retrieval by that key.
// This is a primitive operation, not an iterative one.
// The result is a boolean indicating weather the key allready existed or not. If it allready existed, the value is replaced.
func ( kn *KadNode ) Store ( kv * KadKeyValue , result * bool ) os.Error {
	if kn.MyContact.Id != kv.Contact.Id {
		kn.Buckets.Spotted( kv.Contact, Distance( kn.MyContact.Id, kv.Contact.Id ) )
	}
	_, ( *result ) = kn.KeyValues[kv.Key]
	kn.KeyValues[kv.Key] = kv.Value
	kn.KeyTimes[kv.Key] = time.Nanoseconds()
	return nil
}

// The FIND_NODE RPC includes a 160-bit key. The recipient of the RPC returns up to k triples (IP address, port, nodeID) for the contacts that it knows to be closest to the key.
// The recipient must return k triples if at all possible. It may only return fewer than k if it is returning all of the contacts that it has knowledge of.
// This is a primitive operation, not an iterative one.
func ( kn *KadNode ) Find_Node ( keyW * KadKeyWrapper , result * KadFindNodeResult ) os.Error {
	if keyW.Contact.Id != kn.MyContact.Id {
		kn.Buckets.Spotted( keyW.Contact, Distance( kn.MyContact.Id, keyW.Contact.Id ) )
	}
	key := keyW.Key
	ks, error := kn.Buckets.FindBucket( Distance( kn.MyContact.Id, key ) )
	if error != nil {
		return error
	}
	result.Nodes = ks
	return nil
}

// A FIND_VALUE RPC includes a B=160-bit key (64 bit in our case). If a corresponding value is present on the recipient, the associated data is returned. Otherwise the RPC is equivalent to a FIND_NODE and a set of k triples is returned.
// This is a primitive operation, not an iterative one.
func ( kn *KadNode ) Find_Value ( keyW * KadKeyWrapper , result * KadFindValueResult ) os.Error {
	if kn.MyContact.Id != keyW.Contact.Id {
		kn.Buckets.Spotted( keyW.Contact, Distance( kn.MyContact.Id, keyW.Contact.Id ) )
	}
	key := keyW.Key
	value, exists := kn.KeyValues[key]
	// If Key doesn't exists, return k triples.
	if !exists {
		ks, error := kn.Buckets.FindBucket( Distance( kn.MyContact.Id, key ) )
		if error != nil {
			return error
		}
		result.Nodes = ks
	} else { // Else return the value.
		result.Value = value
	}
	return nil
}

/**************************************************************************************************/
/*                                                                                                */
/*                                         STRUCTS FOR RPC                                        */
/*                                                                                                */
/**************************************************************************************************/


type KadKeyValue struct {
	Contact	*KadContact
	Key		uint64
	Value	[]byte
}

func new_KadKeyValue ( contact *KadContact, key uint64, value []byte ) *KadKeyValue {
	return &KadKeyValue{ contact, key, value }
}

type KadKeyWrapper struct {
	Contact	*KadContact
	Key		uint64
}

func new_KadKeyWrapper ( contact *KadContact, key uint64 ) *KadKeyWrapper {
	return &KadKeyWrapper{ contact, key }
}

type KadFindNodeResult struct {
	Nodes	[]*KadContact
}

func new_KadFindNodeResult ( nodes []*KadContact ) *KadFindNodeResult {
	return &KadFindNodeResult{ nodes }
}

type KadFindValueResult struct {
	Value	[]byte
	Nodes	[]*KadContact
}

func new_KadFindValueResult ( value []byte, nodes []*KadContact ) *KadFindValueResult {
	return &KadFindValueResult{ value, nodes }
}

